//
//  ApiProtocol.swift
//  iUtil
//
//  Created by wendachuan on 19/3/22.
//  Copyright © 2019年 wendachuan. All rights reserved.
//

import Foundation
import AFNetworking

/// API接口协议
public protocol ApiProtocol: HttpHelperDelegate {
    
    /// 创建代理对象
    ///
    /// - Returns:
    static func newDelegate() -> ApiProtocolDelegate?
    
    /// 获取访问地址
    ///
    /// - returns: Api访问地址
    static func getApiAddress() -> String?
    
    /// 在请求前注入
    ///
    /// - Parameters:
    ///   - helper:
    ///   - shortAddress: 请求短地址
    ///   - method: http method
    ///   - parameters: url参数
    ///   - requestHeader: 请求头
    ///   - requestObject: 请求对象
    ///   - returns: 新的请求头和对象
    static func injectBeforeRequest(withAddress shortAddress: String, method: HttpHelper.HttpMethod, parameters: [String: Any]?, _ requestHeader: [String : String]?, andRequestObject requestObject: Any?) -> (newParameters: [String: Any]?, newRequestHeader: [String : String]?, newRequestObject:  Any?)
    
    /// 当http请求成功时，调用此函数可以分析返回的对象，解析其中的错误信息
    ///
    /// - parameter response: 返回的请求
    /// - parameter responseObject: 返回的对象
    /// - returns: 解析出的错误
    static func parseResponseObject(_ response: URLResponse, responseObject: Any) -> NSError?

    /// 当API访问错误时，回调此函数解析错误信息
    ///
    /// - parameter response: 返回的请求
    /// - parameter responseObject: 返回的对象
    /// - returns: 解析出的错误
    static func parseResponseObjectWhenFailed(_ response: URLResponse, responseObject: Any) -> NSError?
}

/// APIProtocol的可选接口定义
public extension ApiProtocol {
    
    /// 创建代理对象
    ///
    /// - Returns:
    static func newDelegate() -> ApiProtocolDelegate? {
        return DefaultApiProtocolDelegate.init(protocolType: self)
    }
    
    /// 设置访问ip和端口
    ///
    /// - parameter ipOrDomain:  ip或者域名
    /// - parameter port:        端口
    static func setApiAddress(withIpOrDomain ipOrDomain: String, andPort port: Int? = nil) {}

    /// 设置访问地址
    ///
    /// - parameter address:  访问地址
    static func setApiAddress(_ address: String) {}
    
    /// 在请求前注入
    ///
    /// - Parameters:
    ///   - helper:
    ///   - shortAddress: 请求短地址
    ///   - method: http method
    ///   - parameters: url参数
    ///   - requestHeader: 请求头
    ///   - requestObject: 请求对象
    ///   - returns: 新的请求头和对象
    static func injectBeforeRequest(withAddress shortAddress: String, method: HttpHelper.HttpMethod, parameters: [String: Any]?, _ requestHeader: [String : String]?, andRequestObject requestObject: Any?) -> (newParameters: [String: Any]?, newRequestHeader: [String : String]?, newRequestObject:  Any?) { return (parameters, requestHeader, requestObject) }
    
    /// 当http请求成功时，调用此函数可以分析返回的对象，解析其中的错误信息
    ///
    /// - parameter response: 返回的请求
    /// - parameter responseObject: 返回的对象
    /// - returns: 解析出的错误
    static func parseResponseObject(_ response: URLResponse, responseObject: Any) -> NSError? { return nil }

    /// 当API访问错误时，回调此函数解析错误信息
    ///
    /// - parameter response: 返回的请求
    /// - parameter responseObject: 返回的对象
    /// - returns: 解析出的错误
    static func parseResponseObjectWhenFailed(_ response: URLResponse, responseObject: Any) -> NSError? { return nil }
}

/// APIProtocol的HTTP访问接口
public extension ApiProtocol {
    /// http POST请求(异步)
    ///
    /// - parameter shortAddress: 短地址
    /// - parameter parameters: get请求url携带的参数
    /// - parameter header: http请求头部
    /// - parameter requestObject: 写入到http body中的json对象
    /// - parameter requestSerializationType: http请求序列化类型
    /// - parameter responseSerializationType: http返回值序列化类型
    /// - parameter success: 成功时调用的闭包
    /// - parameter failure: 失败时调用的闭包
    static func post(_ shortAddress: String,
                     parameters: [String: Any]? = nil,
                     header: [String: String]? = nil,
                     requestObject: Any? = nil,
                     requestSerializationType: HttpRequestSerializationType? = nil,
                     responseSerializationType: HttpResponseSerializationType? = nil,
                     success: @escaping (_ responseObject: Any?) -> Void,
                     failure: @escaping (_ error: NSError) -> Void ) {
        if let address = getApiAddress() {
            let httpHelper = HttpHelper(baseUrl: address)
            httpHelper.delegate = newDelegate()
            httpHelper.post(shortAddress,
                            parameters: parameters,
                            header: header,
                            requestObject: requestObject,
                            certificates: getCertificates(),
                            requestSerializationType: requestSerializationType,
                            responseSerializationType: responseSerializationType,
                            success: success,
                            failure: failure)
        } else {
            failure(NSError(errorCode: 0, errorDescription: "API地址为空"))
        }
    }

    /// http GET请求(异步)
    ///
    /// - parameter shortAddress: 短地址
    /// - parameter parameters: get请求url携带的参数
    /// - parameter header: http请求头部
    /// - parameter requestObject: 写入到http body中的json对象
    /// - parameter requestSerializationType: http请求序列化类型
    /// - parameter responseSerializationType: http返回值序列化类型
    /// - parameter success: 成功时调用的闭包
    /// - parameter failure: 失败时调用的闭包
    static func get(_ shortAddress: String,
                    parameters: [String: Any]? = nil,
                    header: [String: String]? = nil,
                    requestObject: Any? = nil,
                    requestSerializationType: HttpRequestSerializationType? = nil,
                    responseSerializationType: HttpResponseSerializationType? = nil,
                    success: @escaping (_ responseObject: Any?) -> Void,
                    failure: @escaping (_ error: NSError) -> Void ) {
        if let address = getApiAddress() {
            let httpHelper = HttpHelper(baseUrl: address)
            httpHelper.delegate = newDelegate()
            httpHelper.get(shortAddress,
                           parameters: parameters,
                           header: header,
                           requestObject: requestObject,
                           certificates: getCertificates(),
                           requestSerializationType: requestSerializationType,
                           responseSerializationType: responseSerializationType,
                           success: success,
                           failure: failure)
        } else {
            failure(NSError(errorCode: 0, errorDescription: "API地址为空"))
        }
    }

    /// http PUT请求(异步)
    ///
    /// - parameter shortAddress: 短地址
    /// - parameter parameters: get请求url携带的参数
    /// - parameter header: http请求头部
    /// - parameter requestObject: 写入到http body中的json对象
    /// - parameter requestSerializationType: http请求序列化类型
    /// - parameter responseSerializationType: http返回值序列化类型
    /// - parameter success: 成功时调用的闭包
    /// - parameter failure: 失败时调用的闭包
    static func put(_ shortAddress: String,
                    parameters: [String: Any]? = nil,
                    header: [String: String]? = nil,
                    requestObject: Any? = nil,
                    requestSerializationType: HttpRequestSerializationType? = nil,
                    responseSerializationType: HttpResponseSerializationType? = nil,
                    success: @escaping (_ responseObject: Any?) -> Void,
                    failure: @escaping (_ error: NSError) -> Void ) {
        if let address = getApiAddress() {
            let httpHelper = HttpHelper(baseUrl: address)
            httpHelper.delegate = newDelegate()
            httpHelper.put(shortAddress,
                           header: header,
                           requestObject: requestObject,
                           certificates: getCertificates(),
                           requestSerializationType: requestSerializationType,
                           responseSerializationType: responseSerializationType,
                           success: success,
                           failure: failure)
        } else {
            failure(NSError(errorCode: 0, errorDescription: "API地址为空"))
        }
    }

    /// http DELETE请求(异步)
    ///
    /// - parameter shortAddress: 短地址
    /// - parameter parameters: get请求url携带的参数
    /// - parameter header: http请求头部
    /// - parameter requestObject: 写入到http body中的json对象
    /// - parameter requestSerializationType: http请求序列化类型
    /// - parameter responseSerializationType: http返回值序列化类型
    /// - parameter success: 成功时调用的闭包
    /// - parameter failure: 失败时调用的闭包
    static func delete(_ shortAddress: String,
                       parameters: [String: Any]? = nil,
                       header: [String: String]? = nil,
                       requestObject: Any? = nil,
                       requestSerializationType: HttpRequestSerializationType? = nil,
                       responseSerializationType: HttpResponseSerializationType? = nil,
                       success: @escaping (_ responseObject: Any?) -> Void,
                       failure: @escaping (_ error: NSError) -> Void ) {
        if let address = getApiAddress() {
            let httpHelper = HttpHelper(baseUrl: address)
            httpHelper.delegate = newDelegate()
            httpHelper.delete(shortAddress,
                              header: header,
                              requestObject: requestObject,
                              certificates: getCertificates(),
                              requestSerializationType: requestSerializationType,
                              responseSerializationType: responseSerializationType,
                              success: success,
                              failure: failure)
        } else {
            failure(NSError(errorCode: 0, errorDescription: "API地址为空"))
        }
    }
}

/// APIProtocol的HTTP访问接口(不使用基地址)
public extension ApiProtocol {
    /// http POST请求(异步)
    ///
    /// - parameter address: 长地址
    /// - parameter parameters: get请求url携带的参数
    /// - parameter header: http请求头部
    /// - parameter requestObject: 写入到http body中的json对象
    /// - parameter requestSerializationType: http请求序列化类型
    /// - parameter responseSerializationType: http返回值序列化类型
    /// - parameter success: 成功时调用的闭包
    /// - parameter failure: 失败时调用的闭包
    static func post(fullAddress address: String,
                     parameters: [String: Any]? = nil,
                     header: [String: String]? = nil,
                     requestObject: Any? = nil,
                     requestSerializationType: HttpRequestSerializationType? = nil,
                     responseSerializationType: HttpResponseSerializationType? = nil,
                     success: @escaping (_ responseObject: Any?) -> Void,
                     failure: @escaping (_ error: NSError) -> Void ) {
        let httpHelper = HttpHelper(baseUrl: "")
        httpHelper.delegate = newDelegate()
        httpHelper.post(address,
                        parameters: parameters,
                        header: header,
                        requestObject: requestObject,
                        certificates: getCertificates(),
                        requestSerializationType: requestSerializationType,
                        responseSerializationType: responseSerializationType,
                        success: success,
                        failure: failure)
    }
    
    /// http GET请求(异步)
    ///
    /// - parameter address: 长地址
    /// - parameter parameters: get请求url携带的参数
    /// - parameter header: http请求头部
    /// - parameter requestObject: 写入到http body中的json对象
    /// - parameter requestSerializationType: http请求序列化类型
    /// - parameter responseSerializationType: http返回值序列化类型
    /// - parameter success: 成功时调用的闭包
    /// - parameter failure: 失败时调用的闭包
    static func get(fullAddress address: String, parameters: [String: Any]? = nil, header: [String: String]? = nil, requestObject: Any? = nil, requestSerializationType: HttpRequestSerializationType? = nil, responseSerializationType: HttpResponseSerializationType? = nil, success: @escaping (_ responseObject: Any?) -> Void, failure: @escaping (_ error: NSError) -> Void ) {
        let httpHelper = HttpHelper(baseUrl: "")
        httpHelper.delegate = newDelegate()
        httpHelper.get(address,
                       parameters: parameters,
                       header: header,
                       requestObject: requestObject,
                       certificates: getCertificates(),
                       requestSerializationType: requestSerializationType,
                       responseSerializationType: responseSerializationType,
                       success: success,
                       failure: failure)
    }
    
    /// http PUT请求(异步)
    ///
    /// - parameter address: 长地址
    /// - parameter parameters: get请求url携带的参数
    /// - parameter header: http请求头部
    /// - parameter requestObject: 写入到http body中的json对象
    /// - parameter requestSerializationType: http请求序列化类型
    /// - parameter responseSerializationType: http返回值序列化类型
    /// - parameter success: 成功时调用的闭包
    /// - parameter failure: 失败时调用的闭包
    static func put(fullAddress address: String, parameters: [String: Any]? = nil, header: [String: String]? = nil, requestObject: Any? = nil, requestSerializationType: HttpRequestSerializationType? = nil, responseSerializationType: HttpResponseSerializationType? = nil, success: @escaping (_ responseObject: Any?) -> Void, failure: @escaping (_ error: NSError) -> Void ) {
        let httpHelper = HttpHelper(baseUrl: "")
        httpHelper.delegate = newDelegate()
        httpHelper.put(address,
                       parameters: parameters,
                       header: header,
                       requestObject: requestObject,
                       certificates: getCertificates(),
                       requestSerializationType: requestSerializationType,
                       responseSerializationType: responseSerializationType,
                       success: success,
                       failure: failure)
    }
    
    /// http DELETE请求(异步)
    ///
    /// - parameter address: 长地址
    /// - parameter parameters: get请求url携带的参数
    /// - parameter header: http请求头部
    /// - parameter requestObject: 写入到http body中的json对象
    /// - parameter requestSerializationType: http请求序列化类型
    /// - parameter responseSerializationType: http返回值序列化类型
    /// - parameter success: 成功时调用的闭包
    /// - parameter failure: 失败时调用的闭包
    static func delete(fullAddress address: String, parameters: [String: Any]? = nil, header: [String: String]? = nil, requestObject: Any? = nil, requestSerializationType: HttpRequestSerializationType? = nil, responseSerializationType: HttpResponseSerializationType? = nil, success: @escaping (_ responseObject: Any?) -> Void, failure: @escaping (_ error: NSError) -> Void ) {
        let httpHelper = HttpHelper(baseUrl: "")
        httpHelper.delegate = newDelegate()
        httpHelper.delete(address,
                          parameters: parameters,
                          header: header,
                          requestObject: requestObject,
                          certificates: getCertificates(),
                          requestSerializationType: requestSerializationType,
                          responseSerializationType: responseSerializationType,
                          success: success,
                          failure: failure)
    }
}

/// ApiProtocolDelegate
public protocol ApiProtocolDelegate: HttpHelperDelegate {
    /// 构造器
    ///
    /// - Parameter type: ApiProtocol类类型
    init(protocolType type: ApiProtocol.Type)
}

/// 默认协议代理
open class DefaultApiProtocolDelegate: ApiProtocolDelegate {
    let protocolType: ApiProtocol.Type
    required public init(protocolType type: ApiProtocol.Type) {
        protocolType = type
    }
    
    /// 在请求前注入
    ///
    /// - Parameters:
    ///   - helper:
    ///   - shortAddress: 请求短地址
    ///   - method: http method
    ///   - parameters 携带在url地址中的参数
    ///   - requestHeader: 请求头
    ///   - requestObject: 请求对象
    open func injectBeforeRequest(_ helper: HttpHelper,
                                  shortAddress: String,
                                  method: HttpHelper.HttpMethod,
                                  parameters: [String: Any]?,
                                  requestHeader: [String : String]?,
                                  requestObject: Any?) -> (newParameters: [String: Any]?, newRequestHeader: [String : String]?, newRequestObject:  Any?) {
        return protocolType.injectBeforeRequest(withAddress: shortAddress, method: method, parameters: parameters, requestHeader, andRequestObject:requestObject)
    }
    
    /// 当http请求成功时，调用此函数可以分析返回的对象，解析其中的错误信息
    ///
    /// - Parameters:
    ///   - helper:
    ///   - response: 返回的请求
    ///   - responseObject: 返回的对象
    ///   - success: 解析成功后调用的闭包
    ///   - failure: 解析失败后调用的闭包
    /// - Returns: 解析出的错误
    open func parseResponse(_ helper: HttpHelper,
                            httpResponse: HTTPURLResponse,
                            responseObject: Any,
                            success: (_ object: Any?) -> Void,
                            failure: (_ error: NSError) -> Void) {
        
        let jsonResponseSerializer = AFJSONResponseSerializer()
        jsonResponseSerializer.acceptableStatusCodes = nil
        jsonResponseSerializer.acceptableContentTypes = nil
        let jsonResponseObject = jsonResponseSerializer.responseObject(for: httpResponse, data: responseObject as? Data, error: nil)
        if let jsonResponseObject = jsonResponseObject {
            if let error = protocolType.parseResponseObject(httpResponse, responseObject: jsonResponseObject) {
                failure(error)
                return
            } else {
                success(jsonResponseObject)
                return
            }
        } else {
            let xmlResponseSerializer = AFXMLParserResponseSerializer()
            xmlResponseSerializer.acceptableStatusCodes = nil
            xmlResponseSerializer.acceptableContentTypes = nil
            if let xmlResponseObject = xmlResponseSerializer.responseObject(for: httpResponse, data: responseObject as? Data, error: nil) as? XMLParser {
                if let error = protocolType.parseResponseObject(httpResponse, responseObject: xmlResponseObject) {
                    failure(error)
                    return
                } else {
                    success(xmlResponseObject)
                    return
                }
            }
        }
        
        var error: NSError!
        if jsonResponseObject != nil {
            var err: NSError? = nil
            if let dict = JsonHelper.require(jsonResponseObject, asType: NSDictionary.self, error: &err) {
                error = NSError(errorCode: httpResponse.statusCode, errorDescription: "\(dict)")
            } else if let arr = JsonHelper.require(jsonResponseObject, asType: NSArray.self, error: &err) {
                error = NSError(errorCode: httpResponse.statusCode, errorDescription: "\(arr.componentsJoined(by: ","))")
            } else {
                error = NSError(errorCode: httpResponse.statusCode, errorDescription: "\(String(describing: jsonResponseObject))")
            }
        } else if let responseData = responseObject as? Data {
            let responseString = String.toNoneNIL(String(data: responseData, encoding: String.Encoding.utf8))
            error = NSError(errorCode: httpResponse.statusCode, errorDescription: "\(responseString)")
        } else {
            error = NSError(errorCode: httpResponse.statusCode, errorDescription: "http返回对象为空")
        }
        failure(error)
    }
    
    /// 当http请求失败时，表明出现了错误，此时回调此函数解析错误信息
    ///
    /// - parameter helper:
    /// - parameter response: 返回的请求
    /// - parameter responseObject: 返回的对象
    /// - returns: 解析出的错误
    open func parseResponseObjectWhenFailed(_ helper: HttpHelper, httpResponse: HTTPURLResponse, responseObject: Any) -> NSError? {
        let jsonResponseSerializer = AFJSONResponseSerializer()
        jsonResponseSerializer.acceptableStatusCodes = nil
        jsonResponseSerializer.acceptableContentTypes = nil
        let jsonResponseObject = jsonResponseSerializer.responseObject(for: httpResponse, data: responseObject as? Data, error: nil)
        if let jsonResponseObject = jsonResponseObject {
            if let error = protocolType.parseResponseObjectWhenFailed(httpResponse, responseObject: jsonResponseObject) {
                return error
            }
        } else {
            let xmlResponseSerializer = AFXMLParserResponseSerializer()
            xmlResponseSerializer.acceptableStatusCodes = nil
            xmlResponseSerializer.acceptableContentTypes = nil
            if let xmlResponseObject = xmlResponseSerializer.responseObject(for: httpResponse, data: responseObject as? Data, error: nil) as? XMLParser {
                if let error = protocolType.parseResponseObjectWhenFailed(httpResponse, responseObject: xmlResponseObject) {
                    return error
                }
            }
        }
        
        var error: NSError!
        if jsonResponseObject != nil {
            var err: NSError? = nil
            if let dict = JsonHelper.require(jsonResponseObject, asType: NSDictionary.self, error: &err) {
                error = NSError(errorCode: httpResponse.statusCode, errorDescription: "\(dict)")
            } else if let arr = JsonHelper.require(jsonResponseObject, asType: NSArray.self, error: &err) {
                error = NSError(errorCode: httpResponse.statusCode, errorDescription: "\(arr.componentsJoined(by: ","))")
            } else {
                error = NSError(errorCode: httpResponse.statusCode, errorDescription: "\(String(describing: jsonResponseObject))")
            }
        } else if let responseData = responseObject as? Data {
            let responseString = String.toNoneNIL(String(data: responseData, encoding: String.Encoding.utf8))
            error = NSError(errorCode: httpResponse.statusCode, errorDescription: "\(responseString)")
        } else {
            error = NSError(errorCode: httpResponse.statusCode, errorDescription: "http返回对象为空")
        }
        return error
    }
}
